<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- Map -->
    <script>
        const m1 = new Map([
            ['firstName', 'ff'],
            ['lastName', 'll'],
            ['age', 10]
        ]);
        m1.set('sex', 'women');
        console.log(m1, m1.has('age'), m1.get('age'));
        m1.delete('sex');
        console.log(m1, 'size = ' + m1.size);
        // m1.clear();
        // console.log(m1);

        /* 遍历方法 */
        // keys(), values(), entries() - 返回键名/键值/所有成员的遍历器
        for (let key of m1.keys()) {
            console.log(key);
        }
        for (let value of m1.values()) {
            console.log(value);
        }
        for (let entire of m1.entries()) {
            console.log(entire);
        }
        // forEach遍历, 第二个参数可以绑定this
        m1.forEach(function (value, key, entrie) {
            console.log(`value=${value}, key=${key}, entrie=${entrie}`);
        });

        /* 与其他数据结构的转换 */
        // Map -> Array
        // 因为entries是默认的遍历器 - 直接把Map转为数组
        console.log('Map to Array -> ' + [...m1]);

        // Array -> Map
        // 传入Map构造函数
        let arr = [[1, 1], [2, 2], [3, 3]];
        console.log(new Map(arr));

        // Map -> Object
        // 非字符串的键名转换为字符串
        function strMapToObj(strMap) {
            let obj = {};
            for (let [k, v] of strMap) {
                obj[k] = v;
            }
            return obj;
        }
        console.log(strMapToObj(m1));

        // Object -> Map
        // 1 - entries()
        console.log(new Map(Object.entries({ a: 1, b: 2, c: 3 })));
        // 2 - 自写函数
        function objToStrMap(obj) {
            let strMap = new Map();
            for (let key of Object.keys(obj)) {
                strMap.set(key, obj[key]);
            }
            return strMap;
        }
        console.log(objToStrMap({ a: 1, b: 2, c: 3 }));

        // Map -> JSON
        // 1 - Map的键名都是字符串, 转为对象JSON
        function strMapToJson(strMap) {
            return JSON.stringify(strMapToObj(strMap));
        }
        console.log(strMapToJson(m1));
        // 2 - Map的键名中有非字符串, 转为数组Json
        function mapToArrayJson(map) {
            return JSON.stringify([...map]);
        }
        console.log(mapToArrayJson(new Map([[true, 1], [2, 3], [{ test: 4 }, 5]])));

        // JSON -> Map
        // 1 - 所有键名都是字符串
        function jsonToStrMap(jsonStr) {
            return objToStrMap(JSON.parse(jsonStr));
        }
        console.log(jsonToStrMap('{"yes": true, "no": false}'));
        // 2 - JSON为数组, 数组本身为多成员数组
        function jsonToMap(jsonStr) {
            return new Map(JSON.parse(jsonStr));
        }
        console.log(jsonToMap('[[true, 123],[{"foo": 123},["abc"]]]'));

    </script>

    <!-- WeakMap -->
    <script>
        let wm = new WeakMap([[{ id: 1 }, 1], [{ id: 2 }, 2]]);
        // WeakMap中的键只能是Object或继承自Object的类型（null除外）
        wm.set({ id: 3 }, 3);
        wm.set(['id4'], 4);
        // wm.set(null, 5); // 报错
        // wm.set("id6", 6); // 报错
        // wm.set(7, 7); // 报错
        // wm.set(Symbol(), 8); // 报错
        console.log(wm);

        // 垃圾回收机制不考虑弱【键名】所引用的对象 - 有助于防止内存泄漏
        let key9 = { id: 'key9' };
        let obj9 = { id: 9 };
        wm.set(key9, obj9);
        // 键值为正常引用 - 不被垃圾回收
        obj9 = null;
        console.log(wm.get(key9)); // {id: 9}
        // 没有指向这个对象的其他引用, key9被垃圾回收
        key9 = null;
        console.log(wm.get(key9)); // undefined
    </script>
</body>

</html>